#include "ET++.ph"
#ifdef __GNUG__
#pragma implementation
#endif

#include "Slider.h"

#include "Class.h"
#include "Look.h"

//---- ThumbMover ------------------------------------------------------------

class ThumbMover: public Command {
	Point thumborigin;
	Slider *slider;
public:
	ThumbMover(const Point&, Slider*);
	Command *TrackMouse(TrackPhase, Point, Point, Point);
	void TrackFeedback(Point, Point, bool)
		{ /* indirect feedback */ }
};

ThumbMover::ThumbMover(const Point &to, Slider *sl) : Command(cIdNone)
{
	slider= sl;
	thumborigin= to;
}

Command *ThumbMover::TrackMouse(TrackPhase atp, Point ap, Point, Point np)
{
	if (atp == eTrackRelease)
		return gNoChanges;
	if (atp == eTrackMove)
		slider->MoveThumb(thumborigin + (np - ap));
	return this;
}

//---- Thumb -------------------------------------------------------------------

NewMetaImpl0(Thumb,VObject);

Thumb::Thumb(Slider *sl, Direction d) : VObject(eSliderThumb)
{
	slider= sl;
	dir= d;
}

void Thumb::Draw(Rectangle)
{
	gLook->ScaleThumbLayout(dir)->Adorn(this, contentRect);
}

Metric Thumb::GetMinSize()
{
	return gLook->ScaleThumbLayout(dir)->GetMinSize(this);
}

Command *Thumb::DoLeftButtonDownCommand(Point, Token, int)
{
	return new ThumbMover(GetOrigin(), slider);
}

GrCursor Thumb::GetCursor(Point)
{
	return (dir == eVert) ? eCrsUpDownArrow : eCrsLeftRightArrow;
}

//---- Slider ------------------------------------------------------------------

NewMetaImpl(Slider,CompositeVObject, (TP(thumb), TE(dir), TB(feedback),
							T(minVal), T(maxVal), T(currVal), T(bubbleSize)));

Slider::Slider(int id, Direction d, bool fb) : CompositeVObject(id, (SeqCollection*) 0)
{
	dir= d;
	currVal= minVal= gPoint0;
	maxVal= 100;
	bubbleSize= gPoint0;
	feedback= fb;
}

Thumb *Slider::MakeThumb()
{
	return new Thumb(this, dir);
}

void Slider::SetContainer(VObject *v)
{
	if (thumb == 0)
		Add(thumb= MakeThumb());
	CompositeVObject::SetContainer(v);
}

void Slider::SetVal(const Point &v, bool redraw)
{
	Point vv(Range(minVal, maxVal, v));
	if (currVal != vv) {
		currVal= vv;
		Update(redraw);
	}
}

void Slider::SetMax(const Point &v, bool redraw)
{
	if (v != maxVal) {
		maxVal= v;
		currVal= Range(minVal, maxVal, currVal);
		Update(redraw);
	}
}

void Slider::SetMin(const Point &v, bool redraw)
{
	if (v != minVal) {
		minVal= v;
		currVal= Range(minVal, maxVal, currVal);
		Update(redraw);
	}
}

void Slider::SetThumbRange(const Point &s, bool redraw)
{
	if (bubbleSize != s) {
		bubbleSize= s;
		Update(redraw);
	}
}

void Slider::SetOrigin(Point at)
{
	VObject::SetOrigin(at);
	Update(FALSE);
}

Metric Slider::GetMinSize()
{
	if (thumb == 0)
		Add(thumb= MakeThumb());
	return gLook->ScaleLayout()->GetMinSize(this);
}

void Slider::Update(bool redraw)
{
	Point e= GetExtent(),
		s= Scale(bubbleSize, e, maxVal);
	Rectangle b;
	if (thumb == 0)
		Add(thumb= MakeThumb());
	b.extent= Range(thumb->GetMinSize().extent, e, s);
	b.origin= GetOrigin() + Scale(currVal, e-b.extent, maxVal-bubbleSize);
	if (thumb->contentRect != b)
		thumb->SetContentRect(b, redraw);
}

void Slider::MoveThumb(const Point &pos)
{
	Point val= Scale(pos-GetOrigin(), maxVal-bubbleSize, GetExtent()-thumb->GetExtent());
	val= Range(minVal, maxVal, val);
	if (feedback)
		SetVal(val, TRUE);
	Control(GetId(), eSliderThumb, &val);
}

void Slider::Draw(Rectangle r)
{
	gLook->ScaleLayout()->Adorn(this, r, 0);
	CompositeVObject::Draw(r);
}

int Slider::Where(const Point &lp)
{
	Rectangle r= thumb->contentRect;

	if (lp.x >= r.NW().x && lp.x <= r.NE().x) {
		if (lp.y < r.origin.y)
			return 1;
		return 2;
	}
	if (lp.y >= r.NW().y && lp.y <= r.SW().y) {
		if (lp.x < r.origin.x)
			return 3;
		return 4;
	}
	return 0;
}

Command *Slider::DoLeftButtonDownCommand(Point lp, Token, int)
{
	switch (Where(lp)) {
	case 1:
	case 3:
		return new PagerCommand(this, eSliderPageUpLeft);
	case 2:
	case 4:
		return new PagerCommand(this, eSliderPageDownRight);
	}
	return gNoChanges;
}

GrCursor Slider::GetCursor(Point lp)
{
	switch (Where(lp)) {
	case 1:
		return eCrsUpArrow;
	case 2:
		return eCrsDownArrow;
	case 3:
		return eCrsLeftArrow;
	case 4:
		return eCrsRightArrow;
	}
	return VObject::GetCursor(lp);
}

//---- PagerCommand ------------------------------------------------------------

PagerCommand::PagerCommand(Slider* s, int p) : Command("pager", eCmdTypeNoChange)
{
	slider= s;
	part= p;
	SetFlag(eCmdIdleEvents);
}

void PagerCommand::TrackFeedback(Point, Point, bool)
{
}

Command *PagerCommand::TrackMouse(TrackPhase atp, Point, Point, Point np)
{
	switch (atp) {
	case eTrackPress:
	case eTrackIdle:
		if (slider->ContainsPoint(np))
			slider->Control(slider->GetId(), part, (void*)0);
		break;
	case eTrackRelease:
		return gNoChanges;
	default:
		break;
	}
	return this;
}

